package model;

import java.util.ArrayList;
import org.apache.log4j.Logger;
import utils.EnumType.UnitInfo;
import utils.EnumType.UnitTime;
import model.exception.TcException;

public class Tc {

	private static final Logger log = Logger.getLogger(Tc.class);
	
	public static enum Element{qdisc,classs,filter};
	public static enum Command{add,del,change,replace,link};
	public static enum Hierarchy{root,parent};
	
	private Element element;
	private Command command;
	private String device;
	private Hierarchy hierarchy;
	String handle;					//Identificativo (handle) della qdisc
	String parentHandle;			//Identificativo (handle) del padre definito solo se
									//hierarchy.name() == parent altrimenti null
	
	ArrayList<Tc> leaf;				//Per implementare gerarchia qdisc
	
	private Property property;
	
	private Bandwidth bandwidth;
	
	public Tc(Element element,Command command,String device,Hierarchy hierarchy,String handle,
				String parentHandle,Property property,Bandwidth bandwidth) throws TcException{
		
		this.element = element;
		this.command = command;
		this.device = device;
		this.hierarchy = hierarchy;
		this.handle = handle;
		
		leaf = new ArrayList<Tc>();
		
		if(hierarchy.name()=="root")
			this.parentHandle = null;
		else if (hierarchy.name()=="parent" && parentHandle!=null)
			this.parentHandle = parentHandle;
		else
			throw new TcException("If Hierarchy is parent then parentHandle can't be null!");
		
		if(bandwidth!=null && property!=null){
			this.addLeaf(new Tc(element, command, device, Hierarchy.parent,"10:","1:1", property, null));
			this.property = property;
		}else if(property!=null)
			this.property = property;
		
		if(bandwidth!=null)
			this.bandwidth = bandwidth;
		
		
		
	}
	
	public void addLeaf(Tc leaf) throws TcException{

		if (leaf.getCommand().name()!="del" && leaf.getHierarchy().name()=="parent" 
											&& isLeaf(this.handle,leaf.getParentHandle()))
		{
			this.leaf.add(leaf);
			this.property = leaf.property;
		}
		else
			throw new TcException("The rules isn't: add,leaf or the root's handle isn't correct!");
		
	}
	
	private boolean isLeaf(String leaf,String parent){
		//verificare se l'handle leaf è figlio dell'handle parent
		return true;
	}
	
	public Command getCommand(){
		return this.command;
	}
	
	public Hierarchy getHierarchy(){
		return this.hierarchy;
	}
	
	public String getParentHandle(){
		return this.parentHandle;
	}
	
	public void setProperty(Property property){
		this.property = property;
	}
	
	public void setBandwidth(Bandwidth bandwidth){
		this.bandwidth = bandwidth;
	}
	
	public String getCompleteCommand() throws TcException{
		
		String command = "";
		
		if(this.command.name()=="del")
			command = getBasicCommand();
		else if (this.bandwidth != null && this.property != null && leaf.size()!=0)
		{
			command =  getBasicCommand() + this.bandwidth.toTokenBucketString() + " && ";
			command +=  leaf.get(0).getBasicCommand() + this.property.toNetemString();
			

		}else if (this.bandwidth != null)
			command = getBasicCommand() + this.bandwidth.toTokenBucketString();
		else if(this.property != null)
			command = getBasicCommand() + this.property.toNetemString();
		else{
			log.warn("Impossible to add Tc Rules, neither Bandwidth nor Property is set!");
		}
		
		return command;
	}
	
	
	private String getBasicCommand() throws TcException{
		
		String hiearchy = "";
		if(this.hierarchy.name()=="root" && this.handle=="")
			hiearchy = this.hierarchy.toString();
		else if(this.hierarchy.name()=="root") 
			hiearchy = this.hierarchy.toString() + " handle " + this.handle + " ";
		else if(this.hierarchy.name()=="parent" && this.parentHandle==null)
			throw new TcException("You must set Parent Handle!");
		else if(this.hierarchy.name()=="parent")
			hiearchy = this.hierarchy.toString() +  " " +this.parentHandle 
												+ " handle " + this.handle + " ";

		return "tc " + 									//tc
				element.toString() +	" "	+			//[qdisc|class|filter]
				command.toString() + " dev " + device +	//...[add|change|replace|link] dev DEV
				" " + hiearchy;						//[parent qdisc-id|root] handle qdisc-id 
	}
	
	
	public static void main(String[] args) throws TcException {
		
		Property property = new Property(new Period(UnitTime.second,23),
											new Period(UnitTime.second,50),
											new Double(0.12),new Double(100),new Double(12));
		Bandwidth bandwidth = new Bandwidth(UnitInfo.megabit,2);
		
		//Solo banda
		Tc tc1 = new Tc(Element.qdisc, Command.add, "id02eth1", Hierarchy.root, "1:0",null, 
				null, bandwidth);
		//Solo proprietà netem
		Tc tc2 = new Tc(Element.qdisc, Command.add, "id02eth1", Hierarchy.root, "1:0",null, 
				property,null);
		
		//Banda e proprietà
		Tc tc3 = new Tc(Element.qdisc, Command.add, "id02eth1", Hierarchy.root, "1:0",null, 
				property, bandwidth);
		//Solo property da aggiungere a solo banda
		Tc tc4 = new Tc(Element.qdisc, Command.add, "id02eth1", Hierarchy.parent, "10:","1:1", 
				property,null);
		//Delete root
		Tc tc5 = new Tc(Element.qdisc, Command.del, "id02eth1", Hierarchy.root, "1:0",null, 
				null,null);
		//Delete
		Tc tc6 = new Tc(Element.qdisc, Command.del, "id02eth1", Hierarchy.parent, "10:","1:1", 
				property,null);
		
		try {
			
			//Solo banda
			System.out.println(tc1.getCompleteCommand());
			//Solo proprietà netem
			System.out.println(tc2.getCompleteCommand());
			//Banda e proprietà
			System.out.println(tc3.getCompleteCommand());
			
			//Solo property da aggiungere a solo banda
			tc1.addLeaf(tc4);
			System.out.println(tc1.getCompleteCommand());
			
			//Delete root
			System.out.println(tc5.getCompleteCommand());
			//Delete parent
			System.out.println(tc6.getCompleteCommand());
			
		} catch (TcException e) {
			e.printStackTrace();
		}
		
		
	}
	
	
}
